package com.voip.Invite;

import javax.media.*;
import javax.media.rtp.InvalidSessionAddressException;
import javax.media.rtp.SessionAddress;
import javax.sdp.*;
import javax.sip.*;
import javax.sip.header.CSeqHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.message.Message;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.Vector;

/**
 * Created by RXJ on 2018/2/1.
 */
public class SipOutboundCall extends SipCall implements RequestListener, ResponseListener, DTMFListener,SilenceListener {

    int remotePort;

    String remoteHost;

    int localMediaPort;

    SipStack sipStack;

    SipFactory sipFactory;

    boolean initialized;

    SilenceDelayThread silenceThread;

    public SipOutboundCall(String host, int port) {
        remoteHost = host;
        remotePort = port;



        sipHandler = SipHandler.getInstance(host, port);

        sipHandler.setRemoteHost(host);
        sipHandler.setRemotePort(port);
        localMediaPort = -1;
        try {
            DatagramSocket socket = new DatagramSocket();
            localMediaPort = socket.getLocalPort();
            socket.disconnect();
            socket.close();
            initRTP(localMediaPort);
        } catch (Exception e1) {
            System.out.println(e1);
        }

        //sipHandler.addRequestListener(this);
        sipHandler.addResponseListener(this);
        sipHandler.addDTMFListener(this);
        addSilenceListener(this);

        sipFactory = SipFactory.getInstance();
        initialized = false;
    }

    /**
     * @throws ParseException
     * @throws PeerUnavailableException
     * @throws InvalidArgumentException
     * @throws TransactionUnavailableException
     * @throws SipException
     */
    void makeCall(String recipient) throws ParseException,
            PeerUnavailableException, InvalidArgumentException,
            TransactionUnavailableException, SipException {
        if (!initialized) {

            sipHandler.init();
            initialized = true;
        }
        sipHandler.setCallID(System.currentTimeMillis()+ "@" + sipHandler.getLocalHost());
        this.recipient = recipient;
        sendInitialInvite(recipient);
    }

    /**
     * @param recipient
     * @throws ParseException
     * @throws PeerUnavailableException
     * @throws InvalidArgumentException
     * @throws TransactionUnavailableException
     * @throws SipException
     */
    private void sendInitialInvite(String recipient) throws ParseException,
            PeerUnavailableException, InvalidArgumentException,
            TransactionUnavailableException, SipException, ParseException {
        System.out.println("Calling " + recipient+" at "+remoteHost);

        ContentTypeHeader contentTypeHeader = null;
        //content type should be application/sdp (not applications)
        //reported by Oleg Shevchenko (Miratech)
        contentTypeHeader = sipFactory.createHeaderFactory()
                .createContentTypeHeader("application", "sdp");

        String sdpContent = "v=0\n" + "o="+sipHandler.getUsername()+" 0 0 IN IP4 "
                + SipHandler.getInstance().getLocalHost() + "\n" + "s=-\n"
                + "c=IN IP4 " + SipHandler.getInstance().getLocalHost() + "\n"
                + "t=0 0\n" + "m=audio " + localMediaPort
                + " RTP/AVP 4 3 0 5 6 8 15 18\n" + "a=sendrecv\n"
                + "a=rtpmap:101 telephone-event/8000 \n"
                + "a=fmtp:101 64\n"
                + "a=rtpmap:0 PCMU/8000\n";
        System.out.println("sdpContent ="+sdpContent);

        sipHandler.sendRequest(recipient, Request.INVITE, sdpContent, contentTypeHeader);

    }

    public void handleRequest(RequestEvent requestEvent) {
        Request request =requestEvent.getRequest();
        System.out.println("Got request in SipOutboundCall "+request);
        String method = ((CSeqHeader) request.getHeader(CSeqHeader.NAME)).getMethod();

        if (method.equals (Request.BYE)) {
            hangup();
        }
    }

    public void handleResponse(ResponseEvent responseEvent) {
        Response response = responseEvent.getResponse();
        System.out.println("Got response in SipOutboundCall "+response);
        String method = ((CSeqHeader) response.getHeader(CSeqHeader.NAME))
                .getMethod();

        ClientTransaction clientTransaction = responseEvent
                .getClientTransaction();

        try {
            if (response.getStatusCode() == Response.OK) {
                if (method.equals(Request.INVITE)) {
                    if (clientTransaction != null) {
                        Request ack = (Request) clientTransaction.getDialog()
                                .createRequest(Request.ACK);
                        clientTransaction.getDialog().sendAck(ack);

                        startConversation(response, clientTransaction);
                    }
                } else if (method.equals(Request.ACK)) {
                    System.out.println("Got ack");
                }
            } else if ((response.getStatusCode() == Response.BUSY_HERE) ||
                    (response.getStatusCode() == Response.BUSY_EVERYWHERE)) {
                hangup();
            }
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    /**
     * @param response
     * @param clientTransaction
     * @throws SipException
     * @throws SdpParseException
     * @throws SdpException
     * @throws IOException
     * @throws NoDataSourceException
     * @throws NoProcessorException
     * @throws InterruptedException
     * @throws NotConfiguredError
     * @throws NotRealizedError
     * @throws NoDataSinkException
     * @throws InvalidSessionAddressException
     * @throws UnknownHostException
     */
    void startConversation(Message response,
                           Transaction clientTransaction) throws SipException,
            SdpParseException, SdpException, IOException,
            NoDataSourceException, NoProcessorException, InterruptedException,
            NotConfiguredError, NotRealizedError, NoDataSinkException,
            InvalidSessionAddressException {
        System.out.println("Starting conversation");

        String sdpData = new String(response.getRawContent());
        SdpFactory sdpFactory = SdpFactory.getInstance();
        SessionDescription sessionDescription = sdpFactory
                .createSessionDescription(sdpData);
        Vector mediaDescriptions = sessionDescription
                .getMediaDescriptions(true);
        System.out.println("We have "+mediaDescriptions.size()+" media descriptions");
        for (int mdNum = 0; mdNum < mediaDescriptions.size(); mdNum++) {
            MediaDescription mediaDescription = (MediaDescription) mediaDescriptions
                    .elementAt(mdNum);
            Media media = mediaDescription.getMedia();
            String proto = media.getProtocol();
            String type = media.getMediaType();
            int port = media.getMediaPort();

            Vector formats = media.getMediaFormats(true);

            if (formats.size() < 1) {
                System.out.println(
                        "In SIP outbound call: No audio formats");
            }
            int sdpFormat = SdpConstants.PCMU;
            try {
                sdpFormat = Integer.parseInt((String) formats.elementAt(0));
            } catch (NumberFormatException nfExc) {
                nfExc.printStackTrace();
            }

            transmitter = new RtpTransmitter(remoteHost, port, sdpFormat);

            startReceiver(remoteHost, port);
            System.out.println("Starting transmitter");
            transmitter = new RtpTransmitter(remoteHost, port, sdpFormat);
            System.out.println("transmitter="+transmitter);
        }
    }

    private void startReceiver(String remoteHost, int localPort)
            throws InvalidSessionAddressException, IOException,
            UnknownHostException {
        System.out.println(
                "Starting conversation on " + localMediaPort+" to "+remoteHost+":"+localPort);
        SessionAddress remoteAddress = new SessionAddress(InetAddress
                .getByName(remoteHost), localPort);
        rtpManager.addTarget(remoteAddress);
    }

    public void handleDTMF(String digit) {
        //transmitter.playFromInputStream(new ByteArrayInputStream(tts.getWav("You pressed "+digit)), "audio.x_wav");
    }

    public void handleSilenceStart() {
        // TODO Auto-generated method stub
        silenceThread = new SilenceDelayThread(this);
        silenceThread.start();
    }

    public void handleSilenceEnd() {
        // TODO Auto-generated method stub
        if (silenceThread != null) {
            silenceThread.stopDelay();
        }
    }

    public static void main(String[] args) throws Exception {
        SipOutboundCall out;
        if (args.length >= 0) {
            out = new SipOutboundCall("192.168.1.218", 5060);
            out.setRegistrarHost("192.168.1.218");
            out.setRegistrarPort(5060);
            out.setRegistrarDomain("asterisk");
            out.setUsername("816");
            out.setPassword("123456");
            System.out.println("Making call");
            out.makeCall("sip:93035689253@sys1a.TelecomMatters.net:5060");
        } else {
            out = new SipOutboundCall("10.10.10.5", 5060);
            out.setUsername("daverudder");
            //out.makeCall("sip:reliableresponse@10.10.10.5:5060");
//   Thread.sleep(4000);
//   char[] phoneNumber = "97025308877".toCharArray();
//   for (int p = 0; p < phoneNumber.length; p++) {
//    out.sendDTMF(Integer.parseInt(phoneNumber[p]+""));
//   }
            out.makeCall("sip:1990@10.10.10.5:5060");
            //out.makeCall("tel:+97205308877");
        }

//  new BufferedReader(new InputStreamReader(System.in)).readLine();
//  out.hangup();
    }
}

class SilenceDelayThread extends Thread {
    SipOutboundCall call;
    boolean stopped;
    public SilenceDelayThread (SipOutboundCall call) {
        this.call = call;
        stopped = false;
    }
    public void run() {
        int seconds = 0;

        System.out.println("Starting silence checker");
        while ((seconds < 15) && (!stopped)) {
            try {
                Thread.sleep(1000);
                seconds++;
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
        if (!stopped) {
            call.hangup();
        }
    }

    public void stopDelay() {
        System.out.println("Stopping silence checker");
        stopped = true;
    }
}
